<script setup lang="ts">
import { ref, onMounted, watch, computed, onBeforeUnmount, nextTick } from 'vue'
import * as echarts from 'echarts/core'
import { HeatmapChart } from 'echarts/charts'
import type { HeatmapSeriesOption } from 'echarts/charts'
import {
  TitleComponent,
  TooltipComponent,
  GridComponent,
  VisualMapComponent,
  ToolboxComponent,
  LegendComponent,
  DataZoomComponent
} from 'echarts/components'
import type {
  TitleComponentOption,
  TooltipComponentOption,
  GridComponentOption,
  VisualMapComponentOption,
  ToolboxComponentOption,
  LegendComponentOption,
  DataZoomComponentOption
} from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'
import type { EChartsType } from 'echarts/core'

// 定义图表选项类型
type ECOption = echarts.ComposeOption<
  | HeatmapSeriesOption
  | TitleComponentOption
  | TooltipComponentOption
  | GridComponentOption
  | VisualMapComponentOption
  | ToolboxComponentOption
  | LegendComponentOption
  | DataZoomComponentOption
>

// 注册必要的组件
echarts.use([
  TitleComponent,
  TooltipComponent,
  GridComponent,
  HeatmapChart,
  VisualMapComponent,
  ToolboxComponent,
  LegendComponent,
  DataZoomComponent,
  CanvasRenderer
])

const props = defineProps({
  data: {
    type: Array,
    required: true,
    default: () => []
  },
  title: {
    type: String,
    default: '热点话题情感分布'
  },
  height: {
    type: String,
    default: '400px'
  },
  theme: {
    type: String,
    default: ''
  }
})

const chartRef = ref<HTMLElement | null>(null)
const chart = ref<EChartsType | null>(null)
let resizeObserver: ResizeObserver | null = null

interface HeatmapItem {
  id: number;
  topic: string;
  time: string;
  sentiment_value: number;
}

// 处理数据，转换为热图需要的格式
const processData = (rawData: HeatmapItem[]) => {
  if (!rawData || rawData.length === 0) return { heatmapData: [], topics: [], times: [] }
  
  // 获取所有话题和时间
  const topics = [...new Set(rawData.map(item => item.topic))]
  
  // 如果话题太多，只取前10个
  const limitedTopics = topics.length > 10 ? topics.slice(0, 10) : topics
  
  // 按小时聚合时间
  const timeMap: Record<string, { time: string; date: string }> = {}
  rawData.forEach(item => {
    const time = new Date(item.time)
    const hour = time.getHours()
    const timeKey = `${hour}:00`
    
    if (!timeMap[timeKey]) {
      timeMap[timeKey] = {
        time: timeKey,
        date: item.time.split(' ')[0]
      }
    }
  })
  
  // 转换为时间数组
  const times = Array.from({ length: 24 }, (_, i) => `${i.toString().padStart(2, '0')}:00`)
  
  // 计算热图数据
  const heatmapData: [number, number, number, number][] = []
  
  limitedTopics.forEach((topic, topicIndex) => {
    times.forEach((time, timeIndex) => {
      // 过滤出当前话题和时间的数据
      const filteredData = rawData.filter(item => {
        const itemHour = new Date(item.time).getHours()
        const timeHour = parseInt(time.split(':')[0])
        return item.topic === topic && itemHour === timeHour
      })
      
      // 计算情感均值
      let sentimentSum = 0
      let count = 0
      
      filteredData.forEach(item => {
        sentimentSum += item.sentiment_value
        count++
      })
      
      const avgSentiment = count > 0 ? sentimentSum / count : 0
      
      // 添加到热图数据 - 注意这里将字符串改为数字，以便正确显示情感值
      heatmapData.push([timeIndex, topicIndex, parseFloat(avgSentiment.toFixed(2)), count])
    })
  })
  
  return {
    heatmapData,
    topics: limitedTopics,
    times
  }
}

// 计算处理后的数据
const chartData = computed(() => {
  return processData(props.data as HeatmapItem[])
})

// 初始化图表
const initChart = () => {
  if (!chartRef.value) return
  
  // 销毁之前的实例
  if (chart.value) {
    chart.value.dispose()
  }
  
  // 创建图表实例
  nextTick(() => {
    if (chartRef.value) {
      chart.value = echarts.init(chartRef.value, props.theme)
      
      // 使用ResizeObserver监听容器大小变化
      if (resizeObserver) {
        resizeObserver.disconnect()
      }
      
      resizeObserver = new ResizeObserver(() => {
        if (chart.value) {
          try {
            chart.value.resize()
          } catch (e) {
            console.error('热力图调整大小失败:', e)
          }
        }
      })
      
      if (chartRef.value) {
        resizeObserver.observe(chartRef.value)
      }
      
      // 更新图表
      updateChart()
      
      // 确保图表在DOM完全渲染后调整大小
      setTimeout(() => {
        if (chart.value) {
          chart.value.resize()
        }
      }, 200)
    }
  })
}

// 更新图表
const updateChart = () => {
  if (!chart.value) return
  
  try {
    const { heatmapData, topics, times } = chartData.value
    
    const option: ECOption = {
      title: {
        text: props.title,
        left: 'center',
        top: 10,
        textStyle: {
          fontSize: 16,
          fontWeight: 'normal'
        }
      },
      tooltip: {
        position: 'top',
        formatter: function(params: any) {
          const sentiment = params.value[2]
          const count = params.value[3]
          let sentimentType = '中性'
          let color = '#909399'
          
          if (sentiment > 0.3) {
            sentimentType = '正面'
            color = '#67C23A'
          } else if (sentiment < -0.3) {
            sentimentType = '负面'
            color = '#F56C6C'
          }
          
          return `
            <div style="padding: 5px;">
              <div><strong>话题:</strong> ${params.name}</div>
              <div><strong>时间:</strong> ${times[params.value[0]]}</div>
              <div><strong>情感值:</strong> <span style="color:${color}">${sentiment.toFixed(2)}</span> (${sentimentType})</div>
              <div><strong>数据量:</strong> ${count}条</div>
            </div>
          `
        },
        backgroundColor: 'rgba(255, 255, 255, 0.95)',
        borderColor: '#eee',
        borderWidth: 1,
        padding: 10,
        textStyle: {
          color: '#333',
          fontSize: 12
        }
      },
      grid: {
        top: '80px',
        left: '20%',
        right: '15%',
        bottom: '20%',
        containLabel: true
      },
      xAxis: {
        type: 'category',
        data: times,
        splitArea: {
          show: true
        },
        axisLabel: {
          rotate: 45,
          fontSize: 10,
          interval: 1,
          margin: 8
        },
        name: '时间',
        nameLocation: 'middle',
        nameGap: 50,
        nameTextStyle: {
          fontSize: 14,
          padding: [15, 0, 0, 0]
        }
      },
      yAxis: {
        type: 'category',
        data: topics,
        splitArea: {
          show: true
        },
        nameLocation: 'middle',
        nameGap: 80,
        nameTextStyle: {
          fontSize: 14,
          padding: [0, 0, 0, 30]
        },
        axisLabel: {
          fontSize: 12,
          margin: 16,
          width: 120,
          overflow: 'break',
          interval: 0,
          formatter: function(value) {
            return value.length > 10 ? value.substring(0, 10) + '...' : value;
          }
        }
      },
      visualMap: {
        type: 'continuous',
        min: -1,
        max: 1,
        calculable: true,
        orient: 'vertical',
        right: '2%',
        top: 'center',
        inRange: {
          color: ['#F56C6C', '#E6A23C', '#DCDFE6', '#67C23A']
        },
        controller: {
          inRange: {
            color: ['#F56C6C', '#E6A23C', '#DCDFE6', '#67C23A']
          }
        },
        text: ['正面', '负面'],
        textStyle: {
          color: '#333'
        },
        itemWidth: 15,
        itemHeight: 100,
        precision: 2,
        pieces: [
          {min: -1, max: -0.5, color: '#F56C6C'},
          {min: -0.5, max: -0.2, color: '#F89898'},
          {min: -0.2, max: 0.2, color: '#DCDFE6'},
          {min: 0.2, max: 0.5, color: '#95D475'},
          {min: 0.5, max: 1, color: '#67C23A'}
        ]
      },
      toolbox: {
        feature: {
          saveAsImage: { title: '保存为图片' },
          dataView: { title: '数据视图', lang: ['数据视图', '关闭', '刷新'] },
          restore: { title: '还原' },
          dataZoom: { title: { zoom: '区域缩放', back: '还原缩放' } }
        },
        right: '2%',
        top: '10px'
      },
      dataZoom: [
        {
          type: 'slider',
          show: true,
          yAxisIndex: [0],
          start: 0,
          end: 100,
          filterMode: 'filter',
          right: '2%',
          width: 15,
          brushSelect: false,
          handleSize: '80%',
          showDetail: false
        },
        {
          type: 'slider',
          show: true,
          xAxisIndex: [0],
          start: 0,
          end: 100,
          filterMode: 'filter',
          bottom: '10%',
          height: 15,
          brushSelect: false,
          handleSize: '80%',
          showDetail: false
        }
      ],
      series: [
        {
          name: '情感分布',
          type: 'heatmap',
          data: heatmapData,
          label: {
            show: false
          },
          emphasis: {
            itemStyle: {
              shadowBlur: 10,
              shadowColor: 'rgba(0, 0, 0, 0.5)'
            }
          },
          progressive: 100,
          animation: true,
          itemStyle: {
            borderColor: '#fff',
            borderWidth: 1
          }
        }
      ]
    }
    
    chart.value.setOption(option)
  } catch (e) {
    console.error('热力图更新失败:', e)
  }
}

// 监听数据变化
watch(() => props.data, () => {
  updateChart()
}, { deep: true })

// 监听容器大小变化
watch(() => props.height, () => {
  nextTick(() => {
    chart.value && chart.value.resize()
  })
})

onMounted(() => {
  initChart()
})

onBeforeUnmount(() => {
  // 清理资源
  if (resizeObserver) {
    resizeObserver.disconnect()
    resizeObserver = null
  }
  
  if (chart.value) {
    chart.value.dispose()
    chart.value = null
  }
})

// 为了展示效果，创建模拟数据
const createMockData = (): HeatmapItem[] => {
  const topics = ['人工智能', '数据安全', '远程办公', '疫情防控', '元宇宙', '芯片技术', '数字经济']
  const mockData: HeatmapItem[] = []
  
  // 生成24小时内的数据
  const baseDate = new Date('2023-11-10')
  
  for (let hour = 0; hour < 24; hour++) {
    // 为每个话题每小时生成1-5条数据
    topics.forEach(topic => {
      const entriesCount = Math.floor(Math.random() * 5) + 1
      
      for (let i = 0; i < entriesCount; i++) {
        const time = new Date(baseDate)
        time.setHours(hour)
        time.setMinutes(Math.floor(Math.random() * 60))
        
        // 不同话题倾向不同的情感
        let baseSentiment = 0
        switch (topic) {
          case '人工智能':
            baseSentiment = 0.6 // 偏正面
            break
          case '数据安全':
            baseSentiment = -0.5 // 偏负面
            break
          case '远程办公':
            baseSentiment = 0.4 // 较正面
            break
          case '疫情防控':
            baseSentiment = 0.1 // 略正面
            break
          case '元宇宙':
            baseSentiment = 0.3 // 较正面
            break
          case '芯片技术':
            baseSentiment = -0.3 // 略负面
            break
          case '数字经济':
            baseSentiment = 0.5 // 较正面
            break
        }
        
        // 添加一些随机波动
        const sentiment = Math.max(-1, Math.min(1, baseSentiment + (Math.random() * 0.6 - 0.3)))
        
        mockData.push({
          id: mockData.length + 1,
          topic,
          time: time.toISOString().replace('T', ' ').substr(0, 19),
          sentiment_value: parseFloat(sentiment.toFixed(2))
        })
      }
    })
  }
  
  return mockData
}

// 提供一个默认数据的计算属性
const defaultData = computed(() => {
  if (props.data && props.data.length > 0) {
    return props.data
  }
  return createMockData()
})
</script>

<template>
  <div class="sentiment-heatmap-chart" :style="{ height: props.height }">
    <div ref="chartRef" class="chart-container"></div>
  </div>
</template>

<style scoped>
.sentiment-heatmap-chart {
  width: 100%;
  position: relative;
  overflow: hidden;
}

.chart-container {
  width: 100%;
  height: 100%;
  min-height: 600px;
}

@media screen and (max-width: 1200px) {
  .chart-container {
    min-height: 500px;
  }
}

@media screen and (max-width: 768px) {
  .chart-container {
    min-height: 400px;
  }
}
</style> 